#include "netmsg.h"

// message type includes:
#include "connectmsg.h"
#include "connectreplymsg.h"

#include <QDebug>

CNetMsg::CNetMsg(tNetMsgType type)
		: m_nType(type)
{
}


CNetMsg::tNetMsgType CNetMsg::getType() const
{
    return m_nType;
}

quint8 CNetMsg::getProtocolVersion()
{
    return PROTOCOL_VERSION;
}


CNetMsg *CNetMsg::getMsg(QDataStream &s, QString *pErrorStr)
{
    // sanity - make sure the data stream is happy:
    if (s.status() != QDataStream::Ok)
    {
	if (pErrorStr != 0)
	    *pErrorStr = "Datastream status is not OK";
	return NULL;
    }

    // we need to wait for a total of 6 bytes to arrive. That's 2 bytes for the
    // protocol header, plus 4 bytes length information. Don't wait forever though:
    while (s.device()->bytesAvailable() < 6)
    {
        if (!s.device()->waitForReadyRead(500))
        {
            if (pErrorStr)
                *pErrorStr = "Timed out waiting for 6 byte packet header.";
            return false;
        }
    }

    // how do we do this? simple: we know the the first two bytes will always be
    // PROTOCOL_VERSION and MSG_TYPE. We use the protocl version to make sure that
    // we can accept this message (if it's not the same we just return NULL). We
    // then use the message type to work out what sub-class we should create. After
    // creating the sub class, we simply call it's serialisation method to reconstitute
    // it from the data stream, and pass it back to the user.

    char buff[2];
    if (s.readRawData(buff, 2) != 2)
    {
	if (pErrorStr != 0)
	    *pErrorStr = "Could not read 2 byte header block to determine protocol version and message type";
	return NULL;
    }

    if (buff[0] != PROTOCOL_VERSION)
    {
	if (pErrorStr != 0)
	    *pErrorStr = QString("Protocol version does not match: expected %1, got %2 instead").arg(PROTOCOL_VERSION).arg(buff[0]);
	return NULL;
    }

    // now we can read the four-byte size block:
    quint32 nSize;
    s >> nSize;

    qDebug() << "Message size should be" << nSize << "Bytes long";
    while (s.device()->bytesAvailable() < nSize)
    {
        if (!s.device()->waitForReadyRead(500))
        {
            if (pErrorStr)
                *pErrorStr = "Timed out waiting for packet data";
        }
    }
		

    CNetMsg *pMsg =0;

    switch (buff[1])
    {
	case MSG_TYPE_CONNECT:
	{
	    pMsg = new CConnectMsg;
	    pMsg->serialiseFromStream(s);
	}
	break;

	case MSG_TYPE_CONNECT_REPLY:
	{
		pMsg = new CConnectReplyMsg;
		pMsg->serialiseFromStream(s);
	}
	break;
	default:
	Q_ASSERT_X(0, Q_FUNC_INFO, "Unknown message type encountered!");
	if (pErrorStr != 0)
	    *pErrorStr = QString("Unknown message type Id %1").arg(buff[1]);
	return NULL;
    }

    pMsg->m_nType = (tNetMsgType) buff[1];
    return pMsg;
}

bool CNetMsg::setMsg(QDataStream &s, QString *pErrorStr) const
{
    // make sure data stream status is Ok:
    if (s.status() != QDataStream::Ok)
    {
	if (pErrorStr != 0)
	    *pErrorStr = "Data stream status is not Ok";
	return false;
    }

    char buff[2];
    buff[0] = PROTOCOL_VERSION;
    buff[1] = getType();

    if (s.writeRawData(buff, 2) != 2)
    {
	if (pErrorStr != 0)
	    *pErrorStr = "Could not write 2 byte header to data stream";
	return false;
    }

    // tell sub-classes to get their act together and serialise themselves to the stream
    // but get them to write to a QByteArray instead of directly to the socket, since we need to
    // know how much data they're going to write:
    QByteArray arr;
    QDataStream tempDS(&arr, QIODevice::WriteOnly);

    serialiseToStream(tempDS);

    // now write the length to the socket:
    s << (quint32) arr.length();
    s.writeRawData(arr.data(), arr.length());

    return true;
}
